/*
 * Copyright (c) 2011 Imre Fazekas.
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * Neither the name of the Brillien nor the names of its
 * terms and concepts may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.vii.brillien.ignition;

import com.rabbitmq.client.AMQP;
import com.vii.brillien.ignition.classloading.PresenceClassLoader;
import com.vii.brillien.ignition.transport.BrillienMediator;
import com.vii.brillien.ignition.transport.LoaderComplyer;
import com.vii.brillien.ignition.transport.amqp.AmqpMediator;
import com.vii.brillien.ignition.transport.jsonrpc.JsonRpcMediator;
import com.vii.brillien.ignition.transport.mq.MqMediator;
import com.vii.brillien.ignition.transport.xmpp.XmppMediator;
import com.vii.brillien.kernel.BrillienException;
import com.vii.brillien.kernel.axiom.transport.Manager;
import com.vii.streamline.services.IOServices;
import com.vii.streamline.services.ThreadServices;
import com.vii.streamline.services.db.DBServices;
import com.vii.streamline.services.json.JsonServices;
import com.vii.streamline.services.reflection.ReflectionServices;
import com.vii.streamline.structures.collections.InnerList;
import org.slf4j.impl.BrillienLoggerAdapter;

import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Connection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.*;

/**
 * Emvironmental properties of a running Brillien like timout settings, server name, ports values, etc.
 */
public class BrillienContext {

    /**
     * Constant for MQ-based transport layer
     */
    public static final int                     TRANSPORT_MQ        = 1;
    /**
     * Constant for AMQP-based transport layer
     */
    public static final int                     TRANSPORT_AMQP      = 2;
    /**
     * Constant for XMPP-based transport layer
     */
    public static final int                     TRANSPORT_XMPP      = 3;
    /**
     * Constant for JSON-RPC-based transport layer
     */
    public static final int                     TRANSPORT_JSON_RPC  = 4;
    /**
     * Constant for XML-RPC-based transport layer
     */
    public static final int                     TRANSPORT_XML_RPC   = 5;

    /**
     * Root of the classloader hierarchy of Brillien
     */
    public static PresenceClassLoader           primordialClassLoader;

    /**
     * Heartbeat frequency of the Brillien node instance 
     */
    public static long                          HEARTBEAT_PATIENCE;

    /**
     * Timout for flow termination. Exceeding it, the flow is considered as a broken operation and replication is started by the cluster.
     */
    public static long                          FLOW_PATIENCE;


    /**
     * Name of the Brillien instance (must be unique in a cluster)
     */
    public static String                        SERVER_NAME;

    /**
     * Version of the server
     */
    public static double                        SERVER_VERSION;

    public static long                          COMMUNICATION_REPLY_TIMEOUT;

    public static long                          RECONNECTION_TIMEOUT;

    public static long                          RECONNECTION_ATTEMPT;

    public static boolean                       API_CACHING;

    public static boolean                       TEST_MODE;

    /**
     * Configuration of the transport layer
     */
    public static String                        transportType;
    public static String                        transportProvider;
    public static Class                         transportProviderClass;
    public static HashMap<String, String>       transportProviderParameters;
    public static BrillienMediator              mediator;

    /**
     * Timout of a clean shutdown. After the "time is up" message, the unclosed service will be shut down immediately
     */
    public static long                          SHUTDOWN_TOLERANCE;

    /**
     * Size of the thread pool used by the Brillien instance to parallelize the communication.
     */
    public static int                           THREAD_POOL_SIZE;

    /**
     * Parameter for the object pool of a presence.
     * Sets the maximal count of unused presences. Overstepping this limit, the unnecessary instances will be finalized.
     */
    public static int                           MAX_IDLE_PRESENCE_COUNT;
    
    /**
     * Parameter for the object pool of a presence.
     * Sets the initial count of a presence instantiated after publishing.
     */
    public static int                           INITIAL_PRESENCE_COUNT;

    /**
     * Logger Entity for logging activiy of main processes of Brillien instance
     */
    public static Logger                        mainLogger;

    /**
     * Logger handler performing formatting.
     */
    public static Handler                       mainHandler;

    /**
     * Special Handler for exceptional cases. Write directly to the console.(Exception are logged to the mainHandler too.)
     */
    public static Handler                       throwableHandler;

    /**
     * Interest level of the main logger. Every logging request with lower level will be ingnored.
     */
    public static Level                         INTEREST_LEVEL;

    /**
     * Default level of the logging requests.
     */
    public static Level                         DEFAULT_LEVEL;

    /**
     * ID of the flow of Brillien itself 
     */
    public static String                        SUPREME_FLOW_ID;

    /**
     * ID of the root flow of components published in Brillien
     */
    public static String                        SERVICES_FLOW_ID;

    public static boolean                       SSO_ACTIVATED;
    public static String                        SSO_PROVIDER;
    public static HashMap<String, String>       SSO_PARAMETERS;



    public static boolean                       API_KEY_ACTIVATED;
    public static String                        API_KEY_PROVIDER;
    public static HashMap<String, String>       API_KEY_PARAMETERS;


    /**
     * Default autocommit flag for those Transactors who don't specify it.
     */
    public static boolean                       DEFAULT_AUTOCOMMIT;
    
    /**
     * Default isolation level for those Transactors who don't specify it.
     */
    public static int                           DEFAULT_ISOLATION;

    /**
     * Stores all DBVendor info read from config file
     */
    public static HashMap<String, Map>          DB_VENDORS;

    /**
     * Flag for the JMX service of Brillien.
     */
    public static boolean                       JMX_SERVICE_ON;

    /**
     * Port number of the JMX service of Brillien
     */
    public static int                           JMX_SERVICE_PORT;

    /**
     * Tells whether Brillien should clean the Couchdb during Startup process
     */
    public static boolean                       CLEAN_AT_STARTUP;

    /**
     * Flag marking if a couchdb is available for Flow fault-tolerance
     */
    public static boolean                       COUCHDB_BOARD;

    /**
     * URI of the Couchdb instance
     */
    public static String                        COUCHDB_URI;

    /**
     * Timecycle of the heartbeat of the Commander Implementation of Brillien. Measured in milliseconds.
     */
    public static long                          HEARTBEAT_CYCLE_MEASURE;

    /**
     * Timecycle of the cluster-checker Presence watching nodes and flows to be replicated in case of operation failure. Mesured in milliseconds.
     */
    public static long                          KEEPER_CYCLE_MEASURE;

    /**
     * Timecycle of the inner recycle Presence. Mesured in milliseconds.
     */
    public static long                          RETRIEVER_CYCLE_MEASURE;

    /**
     * Timecycle of the component Activator which check if components should be published or republished from the activator directory. Mesured in milliseconds.
     */
    public static long                          ACTIVATOR_CYCLE_MEASURE;

    /**
     * Tells whether the server should send a message to the given recipient about a successful component publishing...
     */
    public static String                        COMPONENT_PUBLISHING_RECIPIENT;


    public static boolean                       WEBSERVER_ON;
    public static String                        WEBSERVER_HOSTNAME;
    public static int                           WEBSERVER_PORT;
    public static String                        WEBSERVER_DOC_ROOT;
    public static List<Map<String, String>>     WEBSERVER_SERVLETS;
    public static boolean                       WEBSERVER_WEBSOCKET_ON;
    public static String                        WEBSERVER_WEBSOCKET_CONTEXT;
    public static List<Map<String, String>>     WEBSERVER_WEBSOCKET_APPLICATINS;

    public static String[][]                    configuration;

    static{
        HEARTBEAT_PATIENCE                      = 10 * 1000;
        FLOW_PATIENCE                           = 1 * 100;

        RECONNECTION_TIMEOUT                    = 5000;
        RECONNECTION_ATTEMPT                    = 3;
        COMMUNICATION_REPLY_TIMEOUT             = 5000;
        API_CACHING                             = true;

        SSO_ACTIVATED                           = false;

        API_KEY_ACTIVATED                       = false;

        primordialClassLoader                   = new PresenceClassLoader(
                                                        ClassLoader.getSystemClassLoader()
                                                  );

        JMX_SERVICE_ON                          = false;

        WEBSERVER_ON                            = false;

        TEST_MODE                               = false;

        WEBSERVER_SERVLETS                      = new InnerList<Map<String, String>>();
        WEBSERVER_WEBSOCKET_APPLICATINS         = new InnerList<Map<String, String>>();
    }

    /**
     * Initializer method to read configuration file of the Brillien application server
     * @param loader PresenceClassLoader instance to manage all classloader-related task
     * @param configuration Map structure of the config file
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     * @throws java.io.IOException
     */
    public static void initServices( PresenceClassLoader loader, Map<String, Object> configuration ) throws BrillienException {
        primordialClassLoader       = loader;


        BrillienLoggerAdapter.mainLogger = mainLogger = BrillienLoggerAdapter.mainLogger == null
                                                        ? Logger.getLogger( SERVER_NAME )
                                                        : BrillienLoggerAdapter.mainLogger;
        mainLogger.setUseParentHandlers( false );
        mainLogger.setLevel( Level.ALL );

        try{
            mainHandler = getHandler( configuration );
            if( mainHandler != null ){
                for(Handler handler : mainLogger.getHandlers() )
                    mainLogger.removeHandler( handler );
                mainLogger.addHandler( mainHandler );

                Formatter formatter = getFormatter( configuration );
                if( formatter != null )
                    mainHandler.setFormatter( formatter );

                throwableHandler = getConsoleHandler( );
                throwableHandler.setFilter( new Filter(){
                    public boolean isLoggable(LogRecord logRecord) {
                        return logRecord != null && logRecord.getMessage() != null && logRecord.getMessage().equals("THROW");
                    }
                } );
                basicLog( Level.CONFIG, "Throwable instances will be directed to console too...");
            }
        } catch (Exception e) {
            throw new BrillienException( "Cannot set-up the logging subsystem.", e );
        }
        try {
            mainHandler.setLevel( INTEREST_LEVEL =
                    (Level) ReflectionServices.getFieldValue( Level.class, Level.class,
                            ReflectionServices.<String>getEmbeddedData( configuration, "debug.interestLevel" )
                    )
            );
        } catch (Exception e) {
            throw new BrillienException( "Cannot set the default interest level of the Logger.", e );
        }

        try {
            DEFAULT_LEVEL = (Level)ReflectionServices.getFieldValue( Level.class, Level.class,
                    ReflectionServices.<String>getEmbeddedData( configuration, "debug.defaultLevel" )
            );
        } catch (Exception e) {
            throw new BrillienException( "Cannot set the default interest level of the Logger.", e );
        }



        SERVER_NAME                     = ReflectionServices.<String>getEmbeddedData( configuration, "serverName" );
        SERVER_VERSION                  = ReflectionServices.<BigDecimal>getEmbeddedData( configuration, "serverVersion" ).doubleValue();
        HEARTBEAT_PATIENCE              = ReflectionServices.<Long>getEmbeddedData( configuration, "heartbeatPatience" );
        FLOW_PATIENCE                   = ReflectionServices.<Long>getEmbeddedData( configuration, "flowPatience" );
        API_CACHING                     = ReflectionServices.<Boolean>getEmbeddedData( configuration, "apiCaching" );
        TEST_MODE                       = ReflectionServices.<Boolean>getEmbeddedData( configuration, "testMode" );
        SHUTDOWN_TOLERANCE              = ReflectionServices.<Long>getEmbeddedData( configuration, "shutdownTolerance" );
        THREAD_POOL_SIZE                = ReflectionServices.<Long>getEmbeddedData( configuration, "threadPoolSize" ).intValue();
        INITIAL_PRESENCE_COUNT          = ReflectionServices.<Long>getEmbeddedData( configuration, "initialPresenceCount" ).intValue();
        MAX_IDLE_PRESENCE_COUNT         = ReflectionServices.<Long>getEmbeddedData( configuration, "maxIdlePresenceCount" ).intValue();
        SUPREME_FLOW_ID                 = ReflectionServices.<String>getEmbeddedData( configuration, "supremeFlowID" );
        SERVICES_FLOW_ID                = ReflectionServices.<String>getEmbeddedData( configuration, "servicesFlowID" );
        HEARTBEAT_CYCLE_MEASURE         = ReflectionServices.<Long>getEmbeddedData( configuration, "heartbeatCycle" );
        KEEPER_CYCLE_MEASURE            = ReflectionServices.<Long>getEmbeddedData( configuration, "keeperCycle" );
        RETRIEVER_CYCLE_MEASURE         = ReflectionServices.<Long>getEmbeddedData( configuration, "retrieverCycle" );
        ACTIVATOR_CYCLE_MEASURE         = ReflectionServices.<Long>getEmbeddedData( configuration, "activatorCycle" );

        try{
            COMPONENT_PUBLISHING_RECIPIENT  = ReflectionServices.<String>getEmbeddedData( configuration, "componentPublishingRecipient" );
        } catch (Exception e){}


        transportType                   = ReflectionServices.<String>getEmbeddedData( configuration, "transportLayer.id" );
        RECONNECTION_TIMEOUT            = ReflectionServices.<Long>getEmbeddedData( configuration, "transportLayer.reconnectionTimeout" );
        RECONNECTION_ATTEMPT            = ReflectionServices.<Long>getEmbeddedData(configuration, "transportLayer.reconnectionAttempt");
        COMMUNICATION_REPLY_TIMEOUT     = ReflectionServices.<Long>getEmbeddedData(configuration, "transportLayer.replyTimeout");
        List<Map> providers             = ReflectionServices.<List<Map>>getEmbeddedData( configuration, "transportLayer.providers" );
        for (Map provider : providers) {
            if( provider.get("name" ).equals( transportType ) ){
                transportProvider       = provider.get("class" ).toString();
                try {
                    transportProviderClass   = primordialClassLoader.loadClass(transportProvider);
                } catch (ClassNotFoundException e) {
                    throw new BrillienException("Cannot load provider class:" + transportProvider);
                }
                if( !BrillienMediator.class.isAssignableFrom( transportProviderClass ) ){
                    throw new BrillienException("All providers has to be a subclass of " + BrillienMediator.class.getSimpleName() );
                }
            }
        }
        if( transportProvider == null )
            throw new BrillienException("No proper transport provider found for:" + transportType);

        transportProviderParameters = new HashMap<String, String>();
        if( transportProviderClass.equals( AmqpMediator.class ) || transportProviderClass.equals( XmppMediator.class ) ){
            transportProviderParameters.put("username", "liaison");
            transportProviderParameters.put("password", "liaison");
        }
        else if( transportProviderClass.equals( MqMediator.class ) ){
            transportProviderParameters.put("queuename", "liaison");
        }
        else if( transportProviderClass.equals( JsonRpcMediator.class ) ){
            transportProviderParameters.put("queuename", "liaison");
            transportProviderParameters.put("url", "http://localhost:8080/jsonrpc/ExchangeServlet");
        }

        Map<String, String> config  = ReflectionServices.<Map>getEmbeddedData( configuration, "transportLayer.config." + transportType );
        for( String key : config.keySet() ){
            transportProviderParameters.put( key, config.get(key).toString() );
        }

        COUCHDB_BOARD               = ReflectionServices.<Boolean>getEmbeddedData( configuration, "couchdb.on");
        CLEAN_AT_STARTUP            = ReflectionServices.<Boolean>getEmbeddedData( configuration, "couchdb.cleanAtStartup");
        COUCHDB_URI                 = COUCHDB_BOARD ? ReflectionServices.<String>getEmbeddedData(configuration, "couchdb.uri") : "localhost";

        JMX_SERVICE_ON              = ReflectionServices.<Boolean>getEmbeddedData(configuration, "jmx.on");
        JMX_SERVICE_PORT            = ReflectionServices.<Long>getEmbeddedData(configuration, "jmx.jmxPort" ).intValue();

        SSO_ACTIVATED               = ReflectionServices.<Boolean>getEmbeddedData( configuration, "sso.on");
        SSO_PROVIDER                = ReflectionServices.<String>getEmbeddedData(configuration, "sso.providerPresence");
        SSO_PARAMETERS              = ReflectionServices.<HashMap<String, String>>getEmbeddedData( configuration, "sso.parameters");

        API_KEY_ACTIVATED           = ReflectionServices.<Boolean>getEmbeddedData( configuration, "apiKey.on");
        API_KEY_PROVIDER            = ReflectionServices.<String>getEmbeddedData( configuration, "apiKey.providerPresence" );
        API_KEY_PARAMETERS          = ReflectionServices.<HashMap<String, String>>getEmbeddedData( configuration, "apiKey.parameters");


        DEFAULT_AUTOCOMMIT          = ReflectionServices.<Boolean>getEmbeddedData( configuration, "dataSource.defaultAutocommit" );
        try {
            DEFAULT_ISOLATION = Connection.class.getField( ReflectionServices.<String>getEmbeddedData( configuration, "dataSource.defaultIsolation") ).getInt( Connection.class );
        } catch (Exception e) {
            throw new BrillienException( "Cannot retrieve isolation from config file.", e );
        }
        DB_VENDORS = new HashMap<String, Map>();
        List<Map> vendors = ReflectionServices.<List<Map>>getEmbeddedData( configuration, "dataSource.vendors");
        for (Map vendor : vendors) {
            try{
                DB_VENDORS.put( vendor.get("name").toString(), vendor );
            } catch(Exception e){ throw new BrillienException( "Invalid DBVendor description", e ); }
        }
        DBServices.initVendors( DB_VENDORS );


            try{
                WEBSERVER_ON = ReflectionServices.<Boolean>getEmbeddedData( configuration, "webserver.on");
                if( WEBSERVER_ON ){
                    WEBSERVER_HOSTNAME = ReflectionServices.<String>getEmbeddedData( configuration, "webserver.hostName");
                    WEBSERVER_PORT = ReflectionServices.<Long>getEmbeddedData(configuration, "webserver.port").intValue();
                    WEBSERVER_DOC_ROOT = ReflectionServices.<String>getEmbeddedData( configuration, "webserver.docRoot");

                    WEBSERVER_SERVLETS = ReflectionServices.<List<Map<String, String>>>getEmbeddedData( configuration, "webserver.servlets");

                    WEBSERVER_WEBSOCKET_ON = ReflectionServices.<Boolean>getEmbeddedData( configuration, "webserver.websockets.on");
                    WEBSERVER_WEBSOCKET_CONTEXT = ReflectionServices.<String>getEmbeddedData( configuration, "webserver.websockets.websocketContext");
                    if( WEBSERVER_WEBSOCKET_ON ){
                        WEBSERVER_WEBSOCKET_APPLICATINS = ReflectionServices.<List<Map<String, String>>>getEmbeddedData( configuration, "webserver.websockets.websocketUniqueApps");
                    }
                }
            } catch(Exception e){ throw new BrillienException( "Invalid Webserver description", e ); }

        fillConfiguration();

        createAccountFor( "slinker" );
    }

    private static void fillConfiguration(){
        List<String[]> list = new LinkedList<String[]>();

        list.add( new String[]{ "HEARTBEAT_PATIENCE", BrillienContext.HEARTBEAT_PATIENCE+"" } );
        list.add( new String[]{ "FLOW_PATIENCE", BrillienContext.FLOW_PATIENCE+"" } );
        list.add( new String[]{ "SERVER_NAME", BrillienContext.SERVER_NAME+"" } );
        list.add( new String[]{ "SERVER_VERSION", BrillienContext.SERVER_VERSION+"" } );
        list.add( new String[]{ "Transport Type", BrillienContext.transportType+"" } );
        list.add( new String[]{ "Transport Provider", BrillienContext.transportProvider+"" } );
        list.add( new String[]{ "Transport Provider Parameters", BrillienContext.transportProviderParameters+"" } );

        list.add( new String[]{ "SHUTDOWN_TOLERANCE", BrillienContext.SHUTDOWN_TOLERANCE+"" } );
        list.add( new String[]{ "THREAD_POOL_SIZE", BrillienContext.THREAD_POOL_SIZE+"" } );
        list.add( new String[]{ "HEARTBEAT_PATIENCE", BrillienContext.HEARTBEAT_PATIENCE+"" } );
        list.add( new String[]{ "MAX_IDLE_PRESENCE_COUNT", BrillienContext.MAX_IDLE_PRESENCE_COUNT+"" } );
        list.add( new String[]{ "INITIAL_PRESENCE_COUNT", BrillienContext.INITIAL_PRESENCE_COUNT+"" } );
        list.add( new String[]{ "INTEREST_LEVEL", BrillienContext.INTEREST_LEVEL+"" } );
        list.add( new String[]{ "DEFAULT_LEVEL", BrillienContext.DEFAULT_LEVEL+"" } );
        list.add( new String[]{ "SUPREME_FLOW_ID", BrillienContext.SUPREME_FLOW_ID+"" } );
        list.add( new String[]{ "SERVICES_FLOW_ID", BrillienContext.SERVICES_FLOW_ID+"" } );
        list.add( new String[]{ "DEFAULT_AUTOCOMMIT", BrillienContext.DEFAULT_AUTOCOMMIT+"" } );
        list.add( new String[]{ "DEFAULT_ISOLATION", BrillienContext.DEFAULT_ISOLATION+"" } );
        list.add( new String[]{ "COUCHDB_BOARD", BrillienContext.COUCHDB_BOARD+"" } );
        list.add( new String[]{ "COUCHDB_URI", BrillienContext.COUCHDB_URI+"" } );
        list.add( new String[]{ "HEARTBEAT_CYCLE_MEASURE", BrillienContext.HEARTBEAT_CYCLE_MEASURE+"" } );
        list.add( new String[]{ "KEEPER_CYCLE_MEASURE", BrillienContext.KEEPER_CYCLE_MEASURE+"" } );
        list.add( new String[]{ "RETRIEVER_CYCLE_MEASURE", BrillienContext.RETRIEVER_CYCLE_MEASURE+"" } );
        list.add( new String[]{ "ACTIVATOR_CYCLE_MEASURE", BrillienContext.ACTIVATOR_CYCLE_MEASURE+"" } );
        list.add( new String[]{ "COMMUNICATION_REPLY_TIMEOUT", BrillienContext.COMMUNICATION_REPLY_TIMEOUT+"" } );
        list.add( new String[]{ "COMPONENT_PUBLISHING_RECIPIENT", BrillienContext.COMPONENT_PUBLISHING_RECIPIENT+"" } );

        configuration = list.toArray( new String[ list.size() ][] );
    }

    private static Formatter getFormatter( Map<String, Object> configuration ) throws IOException {
        String formatterClass = ReflectionServices.<String>getEmbeddedData(configuration, "debug.handler.formatter.className" );
        if( formatterClass.equals("XMLFormatter") ){
            return getXMLFormatter( configuration );
        } else if( formatterClass.equals("SimpleFormatter") ){
            return getSimpleFormatter( configuration );
        }
        return null;
    }
    private static Formatter getXMLFormatter( Map<String, Object> configuration ) throws IOException {
        return new XMLFormatter();
    }
    private static Formatter getSimpleFormatter( Map<String, Object> configuration ) throws IOException {
        return new SimpleFormatter();
    }
    private static Handler getHandler( Map<String, Object> configuration ) throws IOException {
        String handlerClass = ReflectionServices.<String>getEmbeddedData(configuration, "debug.handler.className" );
        if( handlerClass.equals("FileHandler") ){
            return getFileHandler( configuration );
        } else if( handlerClass.equals("ConsoleHandler") ){
            return getConsoleHandler( );
        } else if( handlerClass.equals("SocketHandler") ){
            return getSocketHandler( configuration );
        }
        return null;
    }
    private static Handler getFileHandler( Map<String, Object> configuration ) throws IOException {
        try{
            return new FileHandler(
                ReflectionServices.<String>getEmbeddedData(configuration, "debug.handler.parameters.pattern" ),
                ReflectionServices.<Long>getEmbeddedData(configuration, "debug.handler.parameters.limit" ).intValue(),
                ReflectionServices.<Long>getEmbeddedData(configuration, "debug.handler.parameters.count" ).intValue()
            );
        } finally{
            basicLog( Level.CONFIG, "Logs will be directed to file: " + ReflectionServices.<String>getEmbeddedData(configuration, "debug.handler.parameters.pattern" ) );
        }
    }
    private static Handler getSocketHandler( Map<String, Object> configuration ) throws IOException {
        try{
            return new SocketHandler(
                ReflectionServices.<String>getEmbeddedData(configuration, "debug.handler.parameters.host" ),
                ReflectionServices.<Long>getEmbeddedData(configuration, "debug.handler.parameters.port" ).intValue()
            );
        } finally{
            basicLog( Level.CONFIG, "Logs will be directed to socket: " + ReflectionServices.<String>getEmbeddedData(configuration, "debug.handler.parameters.host" ) + ":" + ReflectionServices.<Long>getEmbeddedData(configuration, "debug.handler.parameters.port" ) );
        }
    }
    private static Handler getConsoleHandler( ) throws IOException {
        try{
            ConsoleHandler c = new ConsoleHandler();
            c.setLevel( Level.ALL );
            return c;
        } finally{
            basicLog( Level.CONFIG, "Logs will be directed to console." );
        }
    }


    /**
     * The logger method used by inner services of the application server
     * @param level level of the information to be logged
     * @param msg message of the log record
     * @param parameters parameters of the log record
     */
    public static void basicLog( Level level, String msg, Object... parameters ){
        if( mainLogger != null )
            mainLogger.logp( level, "Brillien System", "SystemLog", msg, parameters );
        else{
            System.out.println( "Message:" + msg );
            for( Object s : parameters )
                System.out.println("\tParameter:" + s );
        }
    }

    /**
     * Initiates the transport functionality by the given embedded type of transport layer.
     * Used for desktop Brillien
     */
    public static void initTransportLayer( int transportLayerType ){
        initTransportLayer( transportLayerType, null );
    }

    /**
     * Initiates the transport functionality by the given embedded type of transport layer
     * Used for desktop Brillien
     */
    public static void initTransportLayer( int transportLayerType, HashMap<String, String> props ){
        RECONNECTION_TIMEOUT = 5000;
        RECONNECTION_ATTEMPT = 3;
        COMMUNICATION_REPLY_TIMEOUT = 5000;
        transportProviderParameters = new HashMap<String, String>();
        if( transportLayerType == TRANSPORT_MQ ){
            transportType = "mq";
            transportProviderClass = MqMediator.class;
            transportProviderParameters.put("queuename", "liaison");
        }
        else if( transportLayerType == TRANSPORT_AMQP ){
            transportType = "amqp";
            transportProviderClass = AmqpMediator.class;
            transportProviderParameters.put("host", "localhost");
            transportProviderParameters.put("port", AMQP.PROTOCOL.PORT +"" );
            transportProviderParameters.put("username", "liaison");
            transportProviderParameters.put("password", "liaison");
        }
        else if( transportLayerType == TRANSPORT_XMPP ){
            transportType = "xmpp";
            transportProviderClass = XmppMediator.class;
            transportProviderParameters.put("host", "127.0.0.1");
            transportProviderParameters.put("port", "5222");
            transportProviderParameters.put("username", "liaison");
            transportProviderParameters.put("password", "liaison");
        }
        else if( transportLayerType == TRANSPORT_JSON_RPC ){
            transportType = "jsonrpc";
            transportProviderClass = JsonRpcMediator.class;
            transportProviderParameters.put("queuename", "liaison");
            transportProviderParameters.put("url", "http://localhost:8080/jsonrpc/ExchangeServlet");
        }
        if( props != null )
            for( String key : props.keySet() )
                transportProviderParameters.put( key, props.get(key) );
    }

    public static void cleanup(){
        ThreadServices.stop();
    }

    /**
     * Creates a new mediator based on the properties associated with the transport provider
     */
    public static <T extends BrillienMediator> T newMediator() throws BrillienException {
        return BrillienContext.<T>newMediator( transportProviderParameters.get("username"), transportProviderParameters.get("password") );
    }

    /**
     * Creates a new mediator based on the properties associated with the transport provider
     */
    public static <T extends BrillienMediator> T newMediator( String username ) throws BrillienException {
        return BrillienContext.<T>newMediator( username, username );
    }

    /**
     * Creates a new mediator based on the properties associated with the transport provider
     */
    public static <T extends BrillienMediator> T newMediator( String username, String password) throws BrillienException {
        return BrillienContext.<T>newMediator( username, password, null );
    }
    public static <T extends BrillienMediator> T newMediator( String username, String password, LoaderComplyer complyer ) throws BrillienException {
        T mediator = null;
        try {
            mediator = (T)transportProviderClass.newInstance();

            for( String s : transportProviderParameters.keySet() ){
                ReflectionServices.setFieldValueThroughSetMethod(
                        transportProviderClass, mediator,
                        s,
                        (
                                s.equals("username") ? username :
                                        s.equals("password") ? password :
                                                s.equals("queuename") && username != null ? username :
                                                        transportProviderParameters.get(s)
                        )
                );
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new BrillienException( "Cannot create instance" );
        }

        if( complyer != null )
            mediator.setLoaderComplyer( complyer );

        return mediator;
    }

    /**
     * The logger method used by inner services of the application server
     * @param level level of the information to be logged
     * @param msg message of the log record
     * @param parameters parameters of the log record
     */
    public static void systemLog( Level level, String msg, Object... parameters ){
        if( mainLogger != null )
            mainLogger.log( level, msg, parameters );
        else
            System.out.println( msg );
    }

    /**
     * This method used by inner services of the application server to log exceptions
     * @param sourceClass sourceClass
     * @param sourceMethod sourceMethod
     * @param thrown exception occured
     */
    public static void exceptionLog( String sourceClass, String sourceMethod, Throwable thrown ){
        if( mainLogger != null ){
            mainLogger.throwing( sourceClass, sourceMethod, thrown );
        } else
            thrown.printStackTrace();
    }

    public static void createAccountFor(String name) throws BrillienException {
        if( mediator instanceof Manager){
            systemLog( Level.FINE, "Creating account for : " + name );
            BrillienMediator _mediator = newMediator( name, name );
            ((Manager)mediator).createAccountFor( _mediator );
        }
    }


    public static void main(String[] args) throws IOException, BrillienException {
        initServices(
            new PresenceClassLoader( ClassLoader.getSystemClassLoader() ),
            (Map<String, Object>) JsonServices.parseJSON(IOServices.getStreamAsString(IOServices.getInputStream("config.json")))
        );
    }

}
